import struct

from .flash import Flash
from .crc import crc32

HWINFO_BASE_ADDR = 0x308000
HWINFO_DATA_ADDR = 0x308010

HWINFO_V1 = [
    ('PCBM_Version', '<I', 4),
    ('PCBS_Version', '<I', 4),
    ('DSP_Version', '<I', 4),
    ('COSA_Version', '<I', 4),
    ('ITLA_Version', '<I', 4)
]

class HWInfo:
    def __init__(self, b):
        self._f = Flash(b)

    def _load_v1(self, info):
        # info = self._f.fread(HWINFO_DATA_ADDR, self.Length)
        # print(info)
        self.HWInfo = {}
        byte_off = 0
        for entry in HWINFO_V1:
            if (4 == entry[2]):
                self.HWInfo[entry[0]] = struct.unpack(entry[1], info[byte_off:byte_off + entry[2]])[0]
            else:
                self.HWInfo[entry[0]] = list(struct.unpack(entry[1], data[byte_off:byte_off + entry[2]]))
            byte_off += entry[2]

    def load(self):
        header = self._f.fread(HWINFO_BASE_ADDR, 0x10)
        # print(header)
        self.IDWord = struct.unpack('<I', header[0:4])[0]
        self.Version = struct.unpack('<I', header[4:8])[0]
        self.Length = struct.unpack('<I', header[8:12])[0]
        self.Checksum = struct.unpack('<I', header[12:16])[0]
        data = self._f.fread(HWINFO_DATA_ADDR, self.Length)
        if 0x20200814 != self.IDWord:
            self.IDWord = 0x20200814
            self.Version = 1
            self.Length = 20
            self.HWInfo = {}
            self.HWInfo['PCBM_Version'] = 0
            self.HWInfo['PCBS_Version'] = 0
            self.HWInfo['DSP_Version'] = 0
            self.HWInfo['COSA_Version'] = 0
            self.HWInfo['ITLA_Version'] = 0
            return False
        else:
            data = self._f.fread(HWINFO_DATA_ADDR, self.Length)
            if crc32(0xffffffff, data, len(data)) != self.Checksum:
                return false

            if 0x00000001 == self.Version:
                self._load_v1(data)
                return True
            
    def _pack_v1(self, params):
        for entry in HWINFO_V1:
            if (4 == entry[2]):
                params += struct.pack(entry[1], self.HWInfo[entry[0]])
            else:
                params += struct.pack(entry[1], *self.HWInfo[entry[0]])

    def erase(self):
        start_sector = HWINFO_BASE_ADDR // 4096
        self._f.erase_sector(start_sector)

    def save(self):
        params = bytearray()
        
        if 0x00000001 == self.Version:
            self._pack_v1(params)
        # data = self._f.fread(CONFIG_DATA_ADDR, self.Length)
        # print(data == params)
        header = bytearray()
        header += struct.pack('<I', self.IDWord)
        header += struct.pack('<I', self.Version)
        header += struct.pack('<I', len(params))
        header += struct.pack('<I', crc32(0xffffffff, params, len(params)))
        # hdr = self._f.fread(CONFIG_BASE_ADDR, 11)
        # print(header == hdr)
        self.erase()
        self._f.fprogram(HWINFO_BASE_ADDR, header)
        self._f.fprogram(HWINFO_DATA_ADDR, params)